import { Message, Model, Part, Session, SessionStatus, SnapshotFileDiff, UserMessage } from "@opencode-ai/sdk/v2" import { SessionTurn } from "@opencode-ai/ui/session-turn" import { SessionReview } from "@opencode-ai/ui/session-review" import { DataProvider } from "@opencode-ai/ui/context" import { FileComponentProvider } from "@opencode-ai/ui/context/file" import { WorkerPoolProvider } from "@opencode-ai/ui/context/worker-pool" import { createAsync, query, useParams } from "@solidjs/router" import { createMemo, createSignal, ErrorBoundary, For, Match, Show, Switch } from "solid-js" import { Share } from "~/core/share" import { Logo, Mark } from "@opencode-ai/ui/logo" import { IconButton } from "@opencode-ai/ui/icon-button" import { ProviderIcon } from "@opencode-ai/ui/provider-icon" import { iife } from "@opencode-ai/core/util/iife" import { Binary } from "@opencode-ai/core/util/binary" import { NamedError } from "@opencode-ai/core/util/error" import { DateTime } from "luxon" import { createStore } from "solid-js/store" import z from "zod" import NotFound from "../[...404]" import { Tabs } from "@opencode-ai/ui/tabs" import { MessageNav } from "@opencode-ai/ui/message-nav" import { FileSSR } from "@opencode-ai/ui/file-ssr" import { clientOnly } from "@solidjs/start" import { Meta, Title } from "@solidjs/meta" import { Base64 } from "js-base64" import { getRequestEvent } from "solid-js/web" const ClientOnlyWorkerPoolProvider = clientOnly(() => import("@opencode-ai/ui/pierre/worker").then((m) => ({ default: (props: { children: any }) => ( {props.children} ), })), ) const SessionDataMissingError = NamedError.create( "SessionDataMissingError", z.object({ sessionID: z.string(), message: z.string().optional(), }), ) const getData = query(async (shareID) => { "use server" const share = await Share.get(shareID) if (!share) throw new SessionDataMissingError({ sessionID: shareID }) const data = await Share.data(shareID) const result: { sessionID: string shareID: string session: Session[] session_diff: { [sessionID: string]: SnapshotFileDiff[] } session_status: { [sessionID: string]: SessionStatus } message: { [sessionID: string]: Message[] } part: { [messageID: string]: Part[] } model: { [sessionID: string]: Model[] } } = { sessionID: share.sessionID, shareID, session: [], session_diff: { [share.sessionID]: [], }, session_status: { [share.sessionID]: { type: "idle", }, }, message: {}, part: {}, model: {}, } for (const item of data) { switch (item.type) { case "session": result.session.push(item.data) break case "session_diff": result.session_diff[share.sessionID] = item.data break case "message": result.message[item.data.sessionID] = result.message[item.data.sessionID] ?? [] result.message[item.data.sessionID].push(item.data) break case "part": result.part[item.data.messageID] = result.part[item.data.messageID] ?? [] result.part[item.data.messageID].push(item.data) break case "model": result.model[share.sessionID] = item.data break } } const match = Binary.search(result.session, share.sessionID, (s) => s.id) if (!match.found) throw new SessionDataMissingError({ sessionID: share.sessionID }) return result }, "getShareData") export default function () { getRequestEvent()?.response.headers.set( "Cache-Control", "public, max-age=30, s-maxage=300, stale-while-revalidate=86400", ) const params = useParams() const data = createAsync(async () => { if (!params.shareID) throw new Error("Missing shareID") return getData(params.shareID) }) return ( { if (SessionDataMissingError.isInstance(error)) { return } console.error(error) const details = error instanceof Error ? (error.stack ?? error.message) : String(error) return ( Unable to render this share. Check the console for more details. {details} ) }} > {(data) => { const match = createMemo(() => Binary.search(data().session, data().sessionID, (s) => s.id)) if (!match().found) throw new Error(`Session ${data().sessionID} not found`) const info = createMemo(() => data().session[match().index]) const ogImage = createMemo(() => { const models = new Set() const messages = data().message[data().sessionID] ?? [] for (const msg of messages) { if (msg.role === "assistant" && msg.modelID) { models.add(msg.modelID) } } const modelIDs = Array.from(models) const encodedTitle = encodeURIComponent(Base64.encode(encodeURIComponent(info().title.substring(0, 700)))) let modelParam: string if (modelIDs.length === 1) { modelParam = modelIDs[0] } else if (modelIDs.length === 2) { modelParam = encodeURIComponent(`${modelIDs[0]} & ${modelIDs[1]}`) } else if (modelIDs.length > 2) { modelParam = encodeURIComponent(`${modelIDs[0]} & ${modelIDs.length - 1} others`) } else { modelParam = "unknown" } const version = `v${info().version}` return `https://social-cards.sst.dev/opencode-share/${encodedTitle}.png?model=${modelParam}&version=${version}&id=${data().shareID}` }) return ( <> {info().title} | OpenCode {iife(() => { const [store, setStore] = createStore({ messageId: undefined as string | undefined, }) const messages = createMemo(() => data().sessionID ? (data().message[data().sessionID]?.filter((m) => m.role === "user") ?? []).sort( (a, b) => a.time.created - b.time.created, ) : [], ) const firstUserMessage = createMemo(() => messages().at(0)) const activeMessage = createMemo( () => messages().find((m) => m.id === store.messageId) ?? firstUserMessage(), ) function setActiveMessage(message: UserMessage | undefined) { if (message) { setStore("messageId", message.id) } else { setStore("messageId", undefined) } } const provider = createMemo(() => activeMessage()?.model?.providerID) const modelID = createMemo(() => activeMessage()?.model?.modelID) const model = createMemo(() => data().model[data().sessionID]?.find((m) => m.id === modelID())) const diffs = createMemo(() => data().session_diff[data().sessionID] ?? []) const [diffStyle, setDiffStyle] = createSignal<"unified" | "split">("unified") const title = () => ( v{info().version} {model()?.name ?? modelID()} {DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")} {info().title} ) const turns = () => ( {title()} {(message) => ( )} ) const wide = createMemo(() => diffs().length === 0) return ( {title()} 1}> 0}> 0}> Session {diffs().length} Files Changed {turns()} {turns()} ) })} > ) }} ) }
Unable to render this share.
Check the console for more details.
{details}